package auth

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/gin-gonic/gin"
)

//const XRequestID = "X-Request-ID"

var DefaultWriter io.Writer = os.Stdout

var defaultLogFormatter = func(selfParam *SelfLoggerOption, param gin.LogFormatterParams) string {
	var statusColor, methodColor, resetColor string
	if param.IsOutputColor() {
		statusColor = param.StatusCodeColor()
		methodColor = param.MethodColor()
		resetColor = param.ResetColor()
	}

	if param.Latency > time.Minute {
		// Truncate in a golang < 1.8 safe way
		param.Latency = param.Latency - param.Latency%time.Second
	}
	return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s | %s | %d |%s %-7s %s %#v\n%s",
		param.TimeStamp.Format("2006/01/02 - 15:04:05"),
		statusColor, param.StatusCode, resetColor,
		param.Latency,
		param.ClientIP,
		selfParam.RequestId,
		selfParam.OperatorId, // 自定义GIN日志格式，添加操作人用户ID
		methodColor, param.Method, resetColor,
		param.Path,
		param.ErrorMessage,
	)
}

type SelfLoggerOption struct {
	Host       string
	OperatorId int64
	RequestId  string
}

// func MakeRequestID() string {
// 	return fmt.Sprintf("%d", uuid.New().ID())
// }
//
// func NewContextWithRequestID(params ...string) context.Context {
// 	if len(params) > 0 && params[0] != "" {
// 		return context.WithValue(context.Background(), XRequestID, params[0])
// 	} else {
// 		return context.WithValue(context.Background(), XRequestID, MakeRequestID())
// 	}
// }
//
// func GetRequestID(ctx context.Context) string {
// 	if ctx != nil && ctx.Value(XRequestID) != nil {
// 		if val := ctx.Value(XRequestID).(string); val != "" {
// 			return val
// 		}
// 	}
//
// 	return ""
// }

func tracing(c *gin.Context) string {
	reqID := c.GetHeader(XRequestID)
	if reqID == "" {
		c.Request.Header.Add(XRequestID, reqID)
		reqID = MakeRequestID()
	}

	c.Set(XRequestID, reqID)
	c.Writer.Header().Set(XRequestID, reqID)

	return reqID
}

func LoggerWithConfig(conf gin.LoggerConfig) gin.HandlerFunc {
	formatter := defaultLogFormatter

	out := conf.Output
	if out == nil {
		out = DefaultWriter
	}

	notlogged := conf.SkipPaths

	//	isTerm := true
	//
	//	if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||
	//		(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
	//		isTerm = false
	//	}

	var skip map[string]struct{}

	if length := len(notlogged); length > 0 {
		skip = make(map[string]struct{}, length)

		for _, path := range notlogged {
			skip[path] = struct{}{}
		}
	}

	return func(c *gin.Context) {
		// Start timer
		start := time.Now()
		path := c.Request.URL.Path
		raw := c.Request.URL.RawQuery
		var operatorId int64 = 0
		if _, claims, err := GetClaims(c); err == nil && claims != nil {
			operatorId = claims.UserId
		}

		// binding tracing info
		reqID := tracing(c)

		// Process request
		c.Next()

		// Log only when path is not being skipped
		if _, ok := skip[path]; !ok {
			selfParam := &SelfLoggerOption{
				OperatorId: operatorId,
				RequestId:  reqID,
			}

			param := gin.LogFormatterParams{
				Request: c.Request,
				// isTerm:  isTerm,
				Keys: c.Keys,
			}

			// Stop timer
			param.TimeStamp = time.Now()
			param.Latency = param.TimeStamp.Sub(start)

			param.ClientIP = c.ClientIP()
			param.Method = c.Request.Method
			param.StatusCode = c.Writer.Status()
			param.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()

			param.BodySize = c.Writer.Size()

			if raw != "" {
				path = path + "?" + raw
			}

			param.Path = path

			fmt.Fprint(out, formatter(selfParam, param))
		}
	}
}

func Logger() gin.HandlerFunc {
	return LoggerWithConfig(gin.LoggerConfig{})
}
